import path from 'path'
import { scanAssets } from './converter'
import { matchFileType, genFileCssVar, genFileJsVar } from "./utils"

// 插件默认配置
const DEFAULT_OPTIONS = {
    // 默认需要转webp的图片格式
    imageTypes: ['.png', '.jpg', 'jpeg'],
    // 图片资源所在目录
    assets: 'src/assets',
    // 排除目录
    exclude: '',
    // 转成webp的质量系数
    quality: 80,
    // css引用处理方案，false-不处理， true/function-将根据是否支持webp图片为图片定义css变量，并将css文件中的图片地址替换成对应的css变量
    // genCssVar(cssObject, webpCssObject)
    genCssVar: true,
    // js引用处理方案，false-不处理， true/function-将根据是否支持webp图片为图片定义js变量，并将js文件中的图片地址替换成对应的js变量
    // genJsVar(jsObject, webpjsObject)
    genJsVar: true,
    injectPos: '<body>',
    // 主动处理插入脚本
    // 接口定义 inject(html, {cssObject:cssObject, webpCssObject: webpCssObject, jsObject: jsObject, webpJsObject: webpJsObject})
    inject: '',
    baseUrl: '',
    // 是否使用同步模式校验webp支持特性，true - 同步模式，false - 异步
    syncCheckWebpSupport: true,
}

// CSS变量定义对象
// css图片引用示例"/h5open3-jd/dist/assets/video-tip-icon@2x.69ff60ad.png"
let cssObject = {};
// webp js对象，存放转换后图片文件路径信息
let jsObject = {};
// 针对webp环境的css变量定义
let webpCssObject = {};
// 针对webp环境的js变量定义
let webpJsObject = {};

let webpInfo = {}

export default function towebp(custOptions) {

    custOptions = custOptions ? custOptions : {};

    // 合并的配置
    const config = {
        ...DEFAULT_OPTIONS,
        ...custOptions
    }

    // 6位随机串，用于注入脚本变量定义
    const timeStr = new Date().getTime() + ''
    const rStr = timeStr.substring(timeStr.length - 6);

    const cwd = path.resolve("./")
    // 先根据配置的图片目录将目录下所有的图片转成webp，并返回对应的文件名与webp文件的键值对
    webpInfo = scanAssets({assets: path.join(cwd, config.assets), exclude: config.exclude, imageTypes:config.imageTypes, quality: config.quality, cwd: cwd})

    return {
        name: 'vite-plugin-webp',
        configResolved(resolvedConfig) {
            // 存储最终解析的配置
            config.alias = resolvedConfig?.resolve?.alias || []
            config.baseUrl = resolvedConfig.env.BASE_URL
        },
        transformIndexHtml(html) {
            // 增加webp支持判断同时增加对应的标记属性

            if (typeof config.inject === 'function') {
                // 支持主动处理插入脚本
                return config.inject(html, { cssObject: cssObject, webpCssObject: webpCssObject, jsObject: jsObject, webpJsObject: webpJsObject })
            }

            const realTimeSupportBody = `
            var result = document.createElement('canvas').toDataURL('image/webp', 0.5).indexOf('data:image/webp') === 0;
            localStorage.SUPPORT_WEBP = result? 1:2;
            callback(result)
            `

            const asyncSupportBody = `
            var img = new Image();
            img.onload = function(){
                var result = (img.width > 0) && (img.height > 0);
                localStorage.SUPPORT_WEBP = result? 1:2;
                callback(result)
            };
            img.onerror = function(){
                localStorage.SUPPORT_WEBP = 2
                callback(false);
            };
            img.src = '';
            `

            let injectScript = `
            <script type="text/javascript">
            function isSupportWebp${rStr}(callback) {
                try{
                    if(localStorage.SUPPORT_WEBP == 1) return callback(true);
                    if(localStorage.SUPPORT_WEBP == 2) return callback(false);
                    ${config.syncCheckWebpSupport? realTimeSupportBody: asyncSupportBody}
                }catch(e){
                    callback(false)
                }                
            }
            isSupportWebp${rStr}(function(r){
                if(r) {
                    document.querySelector("body").setAttribute("data-webp",1);
                }
            })
            </script>
            `

            if (config.genCssVar) {

                if (typeof config.genCssVar === 'function') {
                    injectScript += config.genCssVar(cssObject, webpCssObject)
                } else {
                    const cssObjectKeys = Object.keys(cssObject)
                    const webpCssObjectKeys = Object.keys(webpCssObject)
                    injectScript += `
                    <style type="text/css">
                        :root{
                            ${cssObjectKeys.map((item) => {
                        return `${item}:url("${cssObject[item]}");`
                    }).join("")
                        }
                        }
                        body[data-webp]{
                            ${webpCssObjectKeys.map((item) => {
                            return `${item}:url("${webpCssObject[item]}");`
                        }).join("")
                        }
                        }
                    </style>
                    `
                }
            }

            if (config.genJsVar) {
                if (typeof config.genJsVar === 'function') {
                    injectScript += config.genJsVar(jsObject, webpJsObject)
                } else {
                    const jsObjectKeys = Object.keys(jsObject)
                    injectScript += `
                    <script type="text/javascript">
                    isSupportWebp${rStr}(function(r){
                        window.webp_${rStr} = {
                            ${jsObjectKeys.map((item) => {
                                return `${item}:r? "${webpJsObject[item]}":"${jsObject[item]}",`
                            }).join("")}
                        }
                    })                    
                    </script>
                    `
                }
            }

            config.injectPos = config.injectPos && typeof config.injectPos === 'string' ? config.injectPos : '<body>'

            return html.replace(config.injectPos, `${config.injectPos} ${injectScript}`)

        },
        async generateBundle(options, bundle) {
            // 包含所有资源文件

            const keys = Object.keys(bundle);

            for (let i = 0; i < keys.length; i++) {

                const fileName = keys[i]
                const b = bundle[fileName];
                const realSourceFileName = path.basename(b.name)
                
                if (webpInfo[realSourceFileName]) {

                    const webpFileInfo = webpInfo[realSourceFileName]
                    const webpIsMin = webpFileInfo.isMin;
                    const webpObj = {
                        fileName: `${b.fileName}.webp`,
                        name: `${b.name}.webp`,
                        type: b.type,
                        source: webpFileInfo.file
                    }

                    let cssVar = '';
                    let jsVar = '';

                    if (config.genCssVar) {
                        cssVar = genFileCssVar(b.fileName)
                        if (cssVar) {
                            cssObject[cssVar] = `${config.baseUrl}${b.fileName}`
                            webpCssObject[cssVar] = `${config.baseUrl}${webpIsMin? webpObj.fileName: b.fileName}`;
                        }
                    }

                    if (config.genJsVar) {
                        jsVar = genFileJsVar(b.fileName)
                        if (jsVar) {
                            jsObject[jsVar] = `${config.baseUrl}${b.fileName}`
                            webpJsObject[jsVar] = `${config.baseUrl}${webpIsMin? webpObj.fileName: b.fileName}`;
                        }
                    }

                    this.emitFile(webpObj);
                }
            }

            // 替换css和js文件中的图片引用
            for (let i = 0; i < keys.length; i++) {
                const fileName = keys[i];
                if (config.genCssVar && matchFileType(fileName, ['.css'])) {
                    console.info(`replace css var in file ${fileName}`);
                    // 将css的对应图片引用换成css变量
                    const cssObjectKeys = Object.keys(cssObject);
                    cssObjectKeys.forEach(item => {
                        const fileUrl = cssObject[item];
                        const source = bundle[fileName].source;
                        if (~source.indexOf(`url("${fileUrl}")`) && webpCssObject[item]) {
                            bundle[fileName].source = source.replaceAll(`url("${fileUrl}")`, `var(${item})`);
                        }
                    });
                }

                if (config.genJsVar && matchFileType(fileName, ['.js'])) {
                    console.info(`replace js var in file ${fileName}`);
                    // 将css的对应图片引用换成css变量
                    const jsObjectKeys = Object.keys(jsObject);
                    jsObjectKeys.forEach(item => {
                        const fileUrl = jsObject[item];
                        const code = bundle[fileName].code
                        if (~code.indexOf(`"${fileUrl}"`) && webpJsObject[item]) {
                            bundle[fileName].code = bundle[fileName].code.replaceAll(`"${fileUrl}"`, `window.webp_${rStr}.${item}`);
                        }
                    });
                }
            }

            return bundle
        }
    }
}